#include "LuaScript.h"

// Initialise all our static pointers to NULL first
// before we do anything, except our LuaState
// LuaState will be handled by lualib
LuaScript	*	LuaScript::LuaScript_Instance	= NULL;
lua_State	*	LuaScript::theLuaState			= lua_open();

LuaScript::LuaScript(void)
{
	// Initialise all the Lua stuff
	luaopen_base(theLuaState);
	luaopen_string(theLuaState);
//	luaopen_table(theLuaState);
//	luaopen_math(theLuaState);
//	luaopen_debug(theLuaState);
	luaL_openlibs(theLuaState);
}

LuaScript::~LuaScript(void)
{
	// Close LuaState
	lua_close(theLuaState);

	// Delete the Instance
	if(LuaScript_Instance != NULL)
	{
		delete LuaScript_Instance;
		LuaScript_Instance = NULL;
	}
}

LuaScript* LuaScript::getInstance(void)
{
	if(LuaScript_Instance == NULL)
		LuaScript_Instance = new LuaScript();

	return LuaScript_Instance;
}

void LuaScript::NewState(void)
{
	theLuaState = luaL_newstate();
}

bool LuaScript::LoadFile(char * FileName)
{
	if(luaL_dofile(theLuaState, FileName) == 1)
	{
		return false;
	}
	return true;
}

void getFuncParamLoop(char * ParamTypes, int & ParamAmount, va_list & Arguments)
{
	// Loop through every parameter
	for(ParamAmount = 0; ParamTypes[ParamAmount] != '\0'; ++ParamAmount)
	{
		union u_ParamType {
			int		b;
			int     i;
			float   f;
			double	d;
			char   *s;
		} thisParam;

		thisParam.b = 0; thisParam.i = 0; thisParam.f = 0; thisParam.s = 0;

		switch( ParamTypes[ParamAmount] )	// Type to expect.
		{
			case 'b':
				thisParam.b = va_arg( Arguments, int);
				lua_pushboolean(LuaScript::getInstance()->theLuaState, thisParam.i);
				break;

			case 'i':
				thisParam.i = va_arg( Arguments, int );
				lua_pushinteger(LuaScript::getInstance()->theLuaState, thisParam.i);
				break;

			case 'f':
				thisParam.f = va_arg( Arguments, float );
				lua_pushnumber(LuaScript::getInstance()->theLuaState, thisParam.f);
				break;

			case 'd':
				thisParam.d = va_arg( Arguments, double );
				lua_pushnumber(LuaScript::getInstance()->theLuaState, thisParam.d);
				break;

			case 's':
				thisParam.s = va_arg( Arguments, char * );
				lua_pushstring(LuaScript::getInstance()->theLuaState, thisParam.s);
				break;

			default:
				cout << "LuaScript\t\t: getFunc - Invalid character passed in for ParamTypes" << endl;
				break;
		}
	}
}

int LuaScript::getFunci(char * FuncName, char * ParamTypes, ...)
{
	int ReturnThis;							// Variable to return
	int ParamAmount;						// Variable to count the number of arguments passed, using the loop

	lua_getglobal(theLuaState, FuncName);	// Tell LuaLib the FuncName from get the file

	va_list arguments;						// A place to store the list of arguments
	va_start (arguments, ParamTypes);		// Initialize arguments to store all values after ParamTypes

	getFuncParamLoop(ParamTypes, ParamAmount, arguments);

	va_end (arguments);						// Clean up the list
	
	lua_call(theLuaState, ParamAmount, 1);	// call the function with 2 arguments, return 1 result

	// Check if value returned is a number
	if(lua_isnumber(theLuaState, -1) == 0)
		cout << "LuaScript\t\t: getFunci - Value returned from Lua function is not a number, returning 0" << endl;

	ReturnThis = lua_tointeger(theLuaState, -1);

	lua_pop(theLuaState, 1);				// pop the top element of the lua stack

	return ReturnThis;
}

float LuaScript::getFuncf(char *FuncName, char *ParamTypes, ...)
{
	float ReturnThis;						// Variable to return
	int ParamAmount;						// Variable to count the number of arguments passed, using the loop

	lua_getglobal(theLuaState, FuncName);	// Tell LuaLib the FuncName from get the file

	va_list arguments;						// A place to store the list of arguments
	va_start (arguments, ParamTypes);		// Initialize arguments to store all values after ParamTypes

	getFuncParamLoop(ParamTypes, ParamAmount, arguments);

	va_end (arguments);						// Clean up the list
	
	lua_call(theLuaState, ParamAmount, 1);	// call the function with 2 arguments, return 1 result

	// Check if value returned is a number
	if(lua_isnumber(theLuaState, -1) == 0)
		cout << "LuaScript\t\t: getFuncf - Value returned from Lua function is not a number, returning 0" << endl;

	// lua_tonumber returns double, we'll cast it to float
	ReturnThis = (float)lua_tonumber(theLuaState, -1);

	lua_pop(theLuaState, 1);				// pop the top element of the lua stack

	return ReturnThis;
}

double LuaScript::getFuncd(char *FuncName, char *ParamTypes, ...)
{
	double ReturnThis;						// Variable to return
	int ParamAmount;						// Variable to count the number of arguments passed, using the loop

	lua_getglobal(theLuaState, FuncName);	// Tell LuaLib the FuncName from get the file

	va_list arguments;						// A place to store the list of arguments
	va_start (arguments, ParamTypes);		// Initialize arguments to store all values after ParamTypes

	getFuncParamLoop(ParamTypes, ParamAmount, arguments);

	va_end (arguments);						// Clean up the list
	
	lua_call(theLuaState, ParamAmount, 1);	// call the function with 2 arguments, return 1 result

	// Check if value returned is a number
	if(lua_isnumber(theLuaState, -1) == 0)
		cout << "LuaScript\t\t: getFuncd - Value returned from Lua function is not a number, returning 0" << endl;

	ReturnThis = lua_tonumber(theLuaState, -1);

	lua_pop(theLuaState, 1);				// pop the top element of the lua stack

	return ReturnThis;
}

char * LuaScript::getFuncs(char *FuncName, char *ParamTypes, ...)
{
	char* ReturnThis;						// Variable to return
	int ParamAmount;						// Variable to count the number of arguments passed, using the loop

	lua_getglobal(theLuaState, FuncName);	// Tell LuaLib the FuncName from get the file

	va_list arguments;						// A place to store the list of arguments
	va_start (arguments, ParamTypes);		// Initialize arguments to store all values after ParamTypes

	getFuncParamLoop(ParamTypes, ParamAmount, arguments);

	va_end (arguments);						// Clean up the list
	
	lua_call(theLuaState, ParamAmount, 1);	// call the function with 2 arguments, return 1 result

	// Check if value returned is a c-string
	if(lua_isstring(theLuaState, -1) == 0)
		cout << "LuaScript\t\t: getFunci - Value returned from Lua function cannot be converted to a c-string, returning 0" << endl;

	ReturnThis = (char*)lua_tostring(theLuaState, -1);

	lua_pop(theLuaState, 1);				// pop the top element of the lua stack

	return ReturnThis;
}

int LuaScript::getFuncb(char *FuncName, char *ParamTypes, ...)
{
	int ReturnThis;							// Variable to return
	int ParamAmount;						// Variable to count the number of arguments passed, using the loop

	lua_getglobal(theLuaState, FuncName);	// Tell LuaLib the FuncName from get the file

	va_list arguments;						// A place to store the list of arguments
	va_start (arguments, ParamTypes);		// Initialize arguments to store all values after ParamTypes

	getFuncParamLoop(ParamTypes, ParamAmount, arguments);

	va_end (arguments);						// Clean up the list
	
	lua_call(theLuaState, ParamAmount, 1);	// call the function with 2 arguments, return 1 result

	// Check if value returned is a boolean
	if(lua_isboolean(theLuaState, -1) == 0)
		cout << "LuaScript\t\t: getFunci - Value returned from Lua function is not a boolean value, returning 0" << endl;

	ReturnThis = lua_toboolean(theLuaState, -1);

	lua_pop(theLuaState, 1);				// pop the top element of the lua stack

	return ReturnThis;
}

int LuaScript::getVari(char * VarName)
{
	lua_getglobal(theLuaState, VarName);

	// Check if value returned is a number
	if(lua_isnumber(theLuaState, -1) == 0)	
		cout << "LuaScript\t\t: getVari - Value retrieved from Lua variable is not a number, returning 0" << endl;

	return lua_tointeger(theLuaState, -1);
}

float LuaScript::getVarf(char * VarName)
{
	lua_getglobal(theLuaState, VarName);

	// Check if value returned is a number
	if(lua_isnumber(theLuaState, -1) == 0)	
		cout << "LuaScript\t\t: getVarf - Value retrieved from Lua variable is not a number, returning 0" << endl;

	return (float)lua_tonumber(theLuaState, -1);
}

double LuaScript::getVard(char * VarName)
{
	lua_getglobal(theLuaState, VarName);

	// Check if value returned is a number
	if(lua_isnumber(theLuaState, -1) == 0)	
		cout << "LuaScript\t\t: getVard - Value retrieved from Lua variable is not a number, returning 0" << endl;

	return lua_tonumber(theLuaState, -1);
}

char * LuaScript::getVars(char * VarName)
{
	lua_getglobal(theLuaState, VarName);

	// Check if value returned is a number
	if(lua_isstring(theLuaState, -1) == 0)	
		cout << "LuaScript\t\t: getVars - Value retrieved from Lua variable cannot be converted to a c-string, returning 0" << endl;

	return (char*)lua_tostring(theLuaState, -1);
}

int LuaScript::getVarb(char * VarName)
{
	lua_getglobal(theLuaState, VarName);

	// Check if value returned is a number
	if(lua_isboolean(theLuaState, -1) == 0)	
		cout << "LuaScript\t\t: getVard - Value retrieved from Lua variable is not a boolean value, returning 0" << endl;

	return lua_toboolean(theLuaState, -1);
}